package com.ucode.springboot.starter.fileStorage.provider.qiniu;

import java.io.IOException;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FetchRet;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.ucode.springboot.starter.fileStorage.FsProviderType;
import com.ucode.springboot.starter.fileStorage.UploadObject;
import com.ucode.springboot.starter.fileStorage.UploadResult;
import com.ucode.springboot.starter.fileStorage.provider.AbstractProvider;
import com.ucode.springboot.starter.fileStorage.utils.FilePathHelper;
import com.ucode.tool.base.ResultCodeEnum;
import com.ucode.tool.exception.UcodeServiceException;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

public class QiniuProvider extends AbstractProvider{

    private static Log log = LogFactory.get();
    
    private static UploadManager uploadManager;//上传工具实例
    private static BucketManager bucketManager;//七牛空间管理实例
    private Auth auth;//认证信息实例
    private boolean isPrivate;//是否私有
    
    /**
     * 构造器
     * @param urlprefix 
     * @param bucketName
     * @param accessKey
     * @param secretKey
     * @param isPrivate
     */
    public QiniuProvider(String clientId,String urlprefix, String bucketName, String accessKey, String secretKey,boolean isPrivate) {
        Assert.notBlank(clientId, "[groupId] not defined");
        Assert.notBlank(bucketName, "[bucketName] not defined");
        Assert.notBlank(accessKey, "[accessKey] not defined");
        Assert.notBlank(secretKey, "[secretKey] not defined");
        Assert.notBlank(urlprefix, "[urlprefix] not defined");
        this.clientId = clientId;
        this.urlprefix = urlprefix.endsWith(FilePathHelper.DIR_SPLITER) ? urlprefix : urlprefix + FilePathHelper.DIR_SPLITER;
        this.bucketName = bucketName;
        auth = Auth.create(accessKey, secretKey);

        /**
         * 华东机房
         * Zone.zone0:华东
         * Zone.zone1:华北
         * Zone.zone2:华南
         * Zone.zoneNa0:北美
         * Zone.autoZone() 自动识别
         */
        Zone region = Zone.autoZone();
        Configuration config = new Configuration(region);
        uploadManager = new UploadManager(config);
        bucketManager = new BucketManager(auth,config);
        this.isPrivate = isPrivate;
    }
    
    /**
     * 上传凭证
     * @date 2020年6月13日
     * @param fileKey 文件Key
     * @param expireSeconds 过期时长
     * @return
     */
    private String getUpToken(String fileKey,long expireSeconds) {
        //参考:https://developer.qiniu.com/kodo/manual/1235/vars#magicvar
        StringMap putPolicy = new StringMap();
        putPolicy.put("returnBody", "{\"bucket\":\"$(bucket)\",\"key\":\"$(key)\",\"etag\":\"$(etag)\",\"fsize\":\"$(fsize)\",\"mimeType\":\"$(mimeType)\",\"height\":$(imageInfo.height),\"width\":\"$(imageInfo.width)\"}");
        return auth.uploadToken(bucketName,fileKey,expireSeconds,putPolicy);
    }


    @Override
    public String providerName() {
        return FsProviderType.qiniu.name();
    }

    @Override
    public UploadResult upload(UploadObject object) {
        String fileName = object.getFileName();
        if(StrUtil.isNotBlank(object.getCatalog())){
            fileName = object.getCatalog().concat(FilePathHelper.DIR_SPLITER).concat(fileName);
        }
        
        try {
            Response res = null;
            String upToken = getUpToken(fileName,3600);
            if(object.getFile() != null){
                res = uploadManager.put(object.getFile(), fileName, upToken);
            }else if(object.getFileBytes() != null){
                res = uploadManager.put(object.getFileBytes(), fileName, upToken);
            }else if(object.getFileInputStream() != null){
                res = uploadManager.put(object.getFileInputStream(), fileName, upToken, null, object.getMimeType());
            }else if(StrUtil.isNotBlank(object.getUrl())){
                //抓取网络资源到空间
                FetchRet fetchRet = bucketManager.fetch(object.getUrl(), bucketName, fileName);
                if(log.isDebugEnabled())
                    log.debug("七牛OSS抓取网络资源结果:"+JSONUtil.toJsonStr(fetchRet));
                return new UploadResult(this.clientId,this.bucketName,this.urlprefix,this.isPrivate,fileName,object.getMimeType());
            }else{
                throw new UcodeServiceException(ResultCodeEnum.FAILED.code, "upload file is NULL");
            }
            PutPolicyRet ret = processUploadResponse(res);
            
            if(log.isDebugEnabled())
                log.debug("七牛OSS上传结果:"+JSONUtil.toJsonStr(ret));
            
            return new UploadResult(this.clientId,this.bucketName,this.urlprefix,this.isPrivate,fileName,object.getMimeType());
        } catch (QiniuException e) {
            processUploadException(fileName, e);
        }
        return null;
    }

    @Override
    public String getDownloadUrl(String fileKey) {
        String path = getFullPath(fileKey);
        if(isPrivate){
            long expireInSeconds = 3600;//1小时，可以自定义链接过期时间
            path = auth.privateDownloadUrl(path, expireInSeconds);
        }
        return path;
    }

    @Override
    public boolean delete(String fileKey) {
        try {
            if (fileKey.contains(FilePathHelper.DIR_SPLITER))
                fileKey = fileKey.replace(urlprefix, "");
            bucketManager.delete(bucketName, fileKey);
            return true;
        } catch (QiniuException e) {
            processUploadException(fileKey, e);
        }
        return false;
    }

    @Override
    public void close() throws IOException {
        
    }
    
    /**
     * 处理上传结果，返回文件url
     * 
     * @return
     * @throws QiniuException
     */
    private PutPolicyRet processUploadResponse(Response res) throws QiniuException {
        if (res.isOK()) {
            PutPolicyRet ret = res.jsonToObject(PutPolicyRet.class);
            return ret;
        }
        throw new UcodeServiceException(ResultCodeEnum.FAILED.code, "{}[{}]",providerName(),res.toString());
    }
    
    private void processUploadException(String fileKey, QiniuException e) {
        Response r = e.response;
        String message;
        try {
            message = r.bodyString();
        } catch (Exception e2) {
            message = r.toString();
        }
        throw new UcodeServiceException(ResultCodeEnum.FAILED.code, "{}[{}]",providerName(),message);
    }

}
